/*
 * app_button.c
 *
 *  Created on: 2023年7月11日
 *      Author: zlw
 */

#include "app_button.h"
#include "app_log.h"
#include "device_type.h"

#define EVENT_CB(ev)   if(handle->cb[ev])handle->cb[ev]((Button*)handle)

/**
  * @brief  Initializes the button struct handle.
  * @param  handle: the button handle strcut.
  * @param  pin_level: read the HAL GPIO of the connet button level.
  * @param  active_level: pressed GPIO level.
  * @param  button_id: the button id.
  * @retval None
  */
void button_init(struct Button* handle, MultiTimer *buttonTimer, MultiTimerCallback_t timerCallback,uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id)
{
  memset(handle, 0, sizeof(struct Button));
  handle->event = (uint8_t)NONE_PRESS;
  handle->hal_button_Level = pin_level;
  handle->active_level = active_level;
  handle->button_id = button_id;
  handle->buttonTimer = buttonTimer;
  handle->timerCallback = timerCallback;
}

/**
  * @brief  Attach the button event callback function.
  * @param  handle: the button handle strcut.
  * @param  event: trigger event type.
  * @param  cb: callback function.
  * @retval None
  */
void button_attach(struct Button* handle, PressEvent event, BtnCallback cb)
{
  handle->cb[event] = cb;
}

/**
* @brief: button状态发生改变时调用次函数
* @param: button结构体指针
*/
#if 0
void button_handler(struct Button* handle)
{
  uint8_t read_gpio_level = handle->hal_button_Level(handle->button_id);

  if(handle->state > 0) {
    handle->ticks ++;
  }
  if(handle->buttonTimer->status != EN_MULTITIMER_STATUS_IDLE)
    softwareMultiTimerStop(BUTTON_TIMER_SOURCE, handle->buttonTimer, 0);
  /*-----------------State machine-------------------*/
  switch (handle->state) {
     case 0:
        if(read_gpio_level == handle->active_level) {   //start press down and debounce
            if(handle->debounce_cnt > DEBOUNCE_CNT) {
                handle->event = (uint8_t)PRESS_DOWN;
                EVENT_CB(PRESS_DOWN);
                handle->ticks = 0;
                handle->state = 1;
                handle->repeat = 1;
                handle->debounce_cnt = 0;
                softwareMultiTimerStart(BUTTON_TIMER_SOURCE, handle->buttonTimer, POLLING_INTERVAL, handle->timerCallback,NULL,0);
            } else {
                handle->debounce_cnt ++;
                handle->state = 0;
                softwareMultiTimerStart(BUTTON_TIMER_SOURCE, handle->buttonTimer, DEBOUNCE_INTERVAL, handle->timerCallback,NULL,0);
            }
        } else {
            handle->debounce_cnt = 0;
            handle->event = (uint8_t)NONE_PRESS;
        }
        break;
     case 1:
      if(read_gpio_level != handle->active_level) {     //released press up
        handle->event = (uint8_t)PRESS_UP;
        EVENT_CB(PRESS_UP);
        handle->ticks = 0;
        handle->state = 2;
      } else if(handle->ticks >= LONG_PRESS_TICKS) {
        handle->event = (uint8_t)LONG_PRESS_START;
        EVENT_CB(LONG_PRESS_START);
        handle->state = 5;
      }
      softwareMultiTimerStart(BUTTON_TIMER_SOURCE, handle->buttonTimer, POLLING_INTERVAL, handle->timerCallback,NULL,0);
      break;

    case 2:
      if(read_gpio_level == handle->active_level) {         //press down again
        handle->event = (uint8_t)PRESS_DOWN;
        EVENT_CB(PRESS_DOWN);
        handle->repeat++;
        EVENT_CB(PRESS_REPEAT); // repeat hit
        handle->ticks = 0;
        handle->state = 3;
        softwareMultiTimerStart(BUTTON_TIMER_SOURCE, handle->buttonTimer, POLLING_INTERVAL, handle->timerCallback,NULL,0);
      } else if(handle->ticks >= SHORT_PRESS_TICKS) {
        if(handle->repeat == 1) {
          handle->event = (uint8_t)SINGLE_CLICK;
          EVENT_CB(SINGLE_CLICK);
        } else if(handle->repeat == 2) {
          handle->event = (uint8_t)DOUBLE_CLICK;
          EVENT_CB(DOUBLE_CLICK); // repeat hit
        }
        handle->state = 0;
      }
      else {
        softwareMultiTimerStart(BUTTON_TIMER_SOURCE, handle->buttonTimer, POLLING_INTERVAL, handle->timerCallback,NULL,0);
      }
      break;

    case 3:
      if(read_gpio_level != handle->active_level) {         //released press up
        handle->event = (uint8_t)PRESS_UP;
        EVENT_CB(PRESS_UP);
        if(handle->ticks < SHORT_PRESS_TICKS) {
          handle->ticks = 0;
          handle->state = 2; //repeat press
          softwareMultiTimerStart(BUTTON_TIMER_SOURCE, handle->buttonTimer, POLLING_INTERVAL, handle->timerCallback,NULL,0);
        } else {
          handle->state = 0;
        }
      }else if(handle->ticks > SHORT_PRESS_TICKS){ // long press up
        handle->state = 0;
      }
      break;

    case 5:
      if(read_gpio_level == handle->active_level) {
        //continue hold trigger
        handle->event = (uint8_t)LONG_PRESS_HOLD;
        EVENT_CB(LONG_PRESS_HOLD);
        softwareMultiTimerStart(BUTTON_TIMER_SOURCE, handle->buttonTimer, POLLING_INTERVAL, handle->timerCallback,NULL,0);
      } else { //releasd
        handle->event = (uint8_t)PRESS_UP;
        EVENT_CB(PRESS_UP);
        handle->state = 0; //reset
      }
      break;
    default:
      handle->state = 0; //reset
      break;
  }
}
#else

void button_handler(struct Button* handle)
{
  uint8_t read_gpio_level = handle->hal_button_Level(handle->button_id);
  uint8_t longPressTick = 0;
  uint8_t longPressInval = 0;

#ifdef SM_EB
  if(handle->button_id == 1) {
      longPressTick = 50;
      longPressInval = longPressTick + 10;
  }
  else {
      longPressTick = LONG_PRESS_TICKS;
      longPressInval = LONG_PRESS_INTERVAL;
  }
#else
  longPressTick = LONG_PRESS_TICKS;
  longPressInval = LONG_PRESS_INTERVAL;
#endif

  if(handle->state > 0) {
    handle->ticks ++;
  }

  /*-----------------State machine-------------------*/
  switch (handle->state) {
     case 0:
        if(read_gpio_level == handle->active_level) {   //start press down and debounce
            if(handle->debounce_cnt > DEBOUNCE_CNT) {
                handle->event = (uint8_t)PRESS_DOWN;
                EVENT_CB(PRESS_DOWN);
                handle->ticks = 0;
                handle->state = 1;
                handle->repeat = 1;
                handle->debounce_cnt = 0;
                softwareMultiTimerStart(BUTTON_TIMER_SOURCE, handle->buttonTimer, POLLING_INTERVAL, handle->timerCallback,NULL,0);
            } else {
                handle->debounce_cnt ++;
                handle->state = 0;
                softwareMultiTimerStart(BUTTON_TIMER_SOURCE, handle->buttonTimer, DEBOUNCE_INTERVAL, handle->timerCallback,NULL,0);
            }
        } else {
            handle->debounce_cnt = 0;
            handle->event = (uint8_t)NONE_PRESS;
        }
        break;
     case 1:
      if(read_gpio_level != handle->active_level) {     //released press up
        handle->event = (uint8_t)PRESS_UP;
        EVENT_CB(PRESS_UP);
        if(handle->ticks >= SHORT_PRESS_TICKS && handle->ticks <= SHORT_PRESS_MAX) {
            handle->event = (uint8_t)SINGLE_CLICK;
            EVENT_CB(SINGLE_CLICK);
        }
        handle->ticks = 0;
        handle->state = 0;
      } else if(handle->ticks >= longPressTick) {
        handle->event = (uint8_t)LONG_PRESS_START;
        EVENT_CB(LONG_PRESS_START);
        handle->state = 5;
        softwareMultiTimerStart(BUTTON_TIMER_SOURCE, handle->buttonTimer, POLLING_INTERVAL, handle->timerCallback,NULL,0);
      } else {
        softwareMultiTimerStart(BUTTON_TIMER_SOURCE, handle->buttonTimer, POLLING_INTERVAL, handle->timerCallback,NULL,0);
      }

      break;

    case 5:
      if(read_gpio_level == handle->active_level) {
        //continue hold trigger
        if(handle->ticks >= longPressInval) {
            handle->event = (uint8_t)LONG_PRESS_HOLD;
            EVENT_CB(LONG_PRESS_HOLD);
            softwareMultiTimerStart(BUTTON_TIMER_SOURCE, handle->buttonTimer, POLLING_INTERVAL/**10*/, handle->timerCallback,NULL,0);/*保持按下检测，可考虑使用内部上拉，定期poll 并打开上拉，以在持续拉低时省电*/
        }
        else
          softwareMultiTimerStart(BUTTON_TIMER_SOURCE, handle->buttonTimer, POLLING_INTERVAL, handle->timerCallback,NULL,0);/*保持按下检测，可考虑使用内部上拉，定期poll 并打开上拉，以在持续拉低时省电*/
      } else { //releasd  TODO 如果按钮始终没有release 耗电将显著增加！需修改并tick超时之
        handle->event = (uint8_t)PRESS_UP;
        EVENT_CB(PRESS_UP);
        handle->state = 0; //reset
      }
      break;
    default:
      handle->state = 0; //reset
      break;
  }
}

#endif
